from tree_sitter import Tree, Node

from typing import List, Callable, Optional
from enum import Enum


class VisitHint(Enum):
    STEP_OVER = 0  # do not visit child nodes


class TreeUtils:

    @staticmethod
    def textof(node: Node) -> str:
        text = node.text
        if text is None:
            return ""

        return text.decode()

    @staticmethod
    def get_child(node: Node, name: str) -> Node:
        child = node.child_by_field_name(name)
        if child is None:
            raise KeyError(f"field {name} does not exist")

        return child

    @classmethod
    def find_children_by_type(cls, node: Node, ty: str) -> List[Node]:
        ret = []
        for child in node.children:
            if child.type == ty:
                ret.append(child)

        return ret

    @classmethod
    def visit(cls, node: Node, visitor: Callable[[Node], Optional[VisitHint]]) -> None:
        queue: List[Node] = [node]
        while queue:
            cursor = queue.pop()
            hint = visitor(cursor)
            if hint is not VisitHint.STEP_OVER:
                queue.extend(cursor.children)
